#include "vi.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/prctl.h>
#include "../slot/slot.h"

//  打开设备
int VI::opendev(string dev, uint8_t permission)
{
    int ret = 0;
    const char *devName = dev.c_str();
    m_fd = open(devName,permission,0655);
    if(m_fd < 0){
        cout<<"open"<<dev<<"failed!"<<endl;
        return -1;
    }
    cout<<"open "<<dev<<endl;
    return ret;
}

//  查询摄像头支持格式
int VI::v4l2_queryfmt()
{
    int ret = 0;
    int i = 0;
    m_fmtavi.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    while(1){
        this->m_fmtavi.index = i;
        i++;
        ret = ioctl(this->m_fd,VIDIOC_ENUM_FMT,&(this->m_fmtavi));
        if(ret < 0){
            perror("无更多参数");
            break;
        }
        cout<<"序号:"<<m_fmtavi.index<<endl;
        cout<<"flags:"<<m_fmtavi.flags<<endl;
        cout<<"描述符:"<<m_fmtavi.description<<endl;
        uint8_t *p = (uint8_t*)&m_fmtavi.pixelformat;
        cout<<"格式:"<<p[0]<<p[1]<<p[2]<<p[3]<<endl;
        cout<<"reserved:"<<m_fmtavi.reserved<<endl;
    }
    return 0;
}

//  设置v4l2格式
int VI::v4l2_setfmt()
{
    int ret = 0;
    memset(&(this->m_fmt),0,sizeof(struct v4l2_format));
    this->m_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //  类型：摄像头采集
    this->m_fmt.fmt.pix.width = this->m_width;      //  采集参数（不可随意填写）
    this->m_fmt.fmt.pix.height = this->m_height;
    this->m_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    this->m_fmt.fmt.pix.bytesperline = this->m_width;
    this->m_fmt.fmt.pix.sizeimage = this->m_width * this->m_height * 2;
    
    ret = ioctl(this->m_fd,VIDIOC_S_FMT,(unsigned long)&(this->m_fmt));
    if(ret < 0){
        perror("设置格式失败!");
        return ret;
    }
    cout<<"设置格式成功"<<endl;
    // memset(&(this->m_fmt),0,sizeof(struct v4l2_format));

    // //  读取设置的格式，查看是否设置成功
    // this->m_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    // ret = ioctl(this->m_fd,VIDIOC_G_FMT,&(this->m_fmt));

    // if((this->m_fmt.fmt.pix.width != this->m_width) || 
    //     (this->m_fmt.fmt.pix.height != this->m_height)||
    //     (this->m_fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)||
    //     (ret < 0)){
    //         cout<<"参数设置错误"<<endl;
    // }
    return ret;
}

//  申请内核缓冲区队列
int VI::v4l2_reqbufs()
{
    int ret = 0;
    memset(&(this->m_reqbufs),0,sizeof(struct v4l2_requestbuffers));
    this->m_reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    this->m_reqbufs.count = this->m_v4l2reqbufcnt;          //  4个缓冲区
#if CAPTURE_USE_USERPTR
    this->m_reqbufs.memory = V4L2_MEMORY_USERPTR;           //  用户空间
#else
    this->m_reqbufs.memory = V4L2_MEMORY_MMAP;              //  映射方式
#endif
    ret = ioctl(this->m_fd,VIDIOC_REQBUFS,&(this->m_reqbufs));
    if(ret < 0){
        perror("request capture buffer form kener failed!");
    }
    return ret;
}

//  将申请到的内核空间映射到用户空间
int VI::v4l2_mmanreqbufs()
{
    int ret =0;
    
    for(int i = 0;i<__MAX_REQBUFS;i++){
        struct video_frame *viframe;
        viframe = &this->capture_frame[i];
        memset(&(viframe->v4l2_buf),0,sizeof(struct v4l2_buffer));
        viframe->v4l2_buf.index = i;
        viframe->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
#if CAPTURE_USE_USERPTR
        viframe->v4l2_buf.memory = V4L2_MEMORY_USERPTR;
#else
        viframe->v4l2_buf.memory = V4L2_MEMORY_MMAP;
#endif

        ret = ioctl(this->m_fd,VIDIOC_QUERYBUF,&(viframe->v4l2_buf));   //  从内核空间中查询一个空间做映射
        if(ret < 0){
            perror("query kernel buffer failed!");
            continue;
        }
#if CAPTURE_USE_USERPTR
#else

        //  映射到用户空间
        this->capture_frame->mmptr[i] = (uint8_t*)mmap(NULL,
                                        viframe->v4l2_buf.length,
                                        PROT_READ|PROT_WRITE,
                                        MAP_SHARED,
                                        this->m_fd,
                                        viframe->v4l2_buf.m.offset);
        if(this->capture_frame->mmptr[i] == MAP_FAILED){
            perror("mmap capture plane buffer failed!");
            continue;
        }
        
        //  查询后通知内核已经放回
        ret = ioctl(this->m_fd,VIDIOC_QBUF,&(viframe->v4l2_buf));
        if(ret < 0){
            perror("release buffer failed!");
            continue;
        }
#endif
        //  将得到的v4l2buff添加到队列中
        this->vi_put_frame(viframe);
    }
    return 0;
}

//  开始采集帧数据
int VI::v4l2_startcapture()
{
    int ret = 0;
    int buftype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(this->m_fd,VIDIOC_STREAMON,&buftype);
    if(ret < 0){
        perror("start capture failed!");
        return ret;
    }
    //  start thread
    ret = pthread_create(&this->m_thread,NULL,vi_capture_plane_handle_pthread,this);
    this->m_run = true;
    return ret;
}

int VI::v4l2_stopcapture()
{
    int ret = 0;
    int buftype = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    this->m_run = false;
    pthread_join(this->m_thread,NULL);
    ret = ioctl(this->m_fd,VIDIOC_STREAMOFF,&buftype);
    if(ret < 0){
        perror("stop capture!");
        return ret;
    }
    return 0;
}

int VI::vi_put_frame(video_frame *pframe)
{
    int ret = 0;
    list_add_tail(&(pframe->head),&(this->free_capture_queue));
    return ret;
}

video_frame *VI::vi_get_frame()
{
    struct video_frame * pframe;
    pframe = nullptr;
    if(!list_empty(&(this->free_capture_queue))){
        pframe = list_first_entry(&(this->free_capture_queue),struct video_frame,head);
        list_del(&(pframe->head));
    }
    return pframe;
}

int VI::getfd()
{
    return this->m_fd;
}

bool VI::isrunning()
{
    return this->m_run;
}

VI::VI()
{
    int ret = 0;
    INIT_LIST_HEAD(&(this->free_capture_queue));
    INIT_LIST_HEAD(&(this->used_capture_queue));
    ret = this->opendev(m_dev,O_RDWR);
    // this->v4l2_queryfmt();
    this->v4l2_setfmt();
    this->v4l2_reqbufs();  
    this->v4l2_mmanreqbufs();

}

VI::~VI()
{
    this->v4l2_stopcapture();
    cout<<"close "<<this->m_dev<<endl;
    close(this->m_fd);
}

//  
static void* vi_capture_mplane_handle_pthread(void *ptr)
{
    int ret = 0;
    class VI *p_vi = (class VI*)ptr;
    struct video_frame *frame;
    struct v4l2_buffer v4l2_buf;
    struct v4l2_plane planes[__MAX_CAPTURE_FRAME_];
    
    int fd = p_vi->getfd();
    prctl(PR_SET_NAME,"vi_capture");
    cout<<"in capture thread!"<<endl;
    while(p_vi->isrunning()){
        struct timeval tv;
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        fd_set read_set;
        FD_ZERO(&read_set);
        FD_SET(fd,&read_set);
        ret = select(FD_SETSIZE,&read_set,NULL,NULL,&tv);
        if(ret < 0){
            perror("select error!");
            break;
        }
        else if(ret == 0){
            perror("select timeout!");
            continue;
        }

        if(FD_ISSET(fd,&read_set)){
            memset(&v4l2_buf,0,sizeof(struct v4l2_buffer));
            v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
            v4l2_buf.memory = V4L2_MEMORY_MMAP;
            v4l2_buf.length = 2;
            v4l2_buf.m.planes = planes;
            ret = ioctl(fd,VIDIOC_DQBUF,&v4l2_buf);
            if(ret < 0){
                perror("vi capture frame failed!");
                continue;
            }
            frame = &(p_vi->capture_frame[v4l2_buf.index]);
            //  将内核空间放回
            ret = ioctl(fd,VIDIOC_QBUF,&v4l2_buf);
            if(ret < 0){
                perror("realse kernel failed!");
                break;
            }
        }
    }
    return nullptr;
}

static void* vi_capture_plane_handle_pthread(void *ptr)
{
    int ret = 0;
    class VI *p_vi = (class VI*)ptr;
    struct video_frame *viframe;
    int fd = p_vi->getfd();

    while(p_vi->isrunning()){
        struct timeval tv;
        fd_set read_set;
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        FD_ZERO(&read_set);
        FD_SET(fd, &read_set);
        ret = select(FD_SETSIZE, &read_set, NULL, NULL, &tv);
        if(ret < 0){
            perror("select error!");
            break;
        }else if(ret == 0){
            perror("select timeout!");
            continue;
        }
        if(FD_ISSET(fd, &read_set)){
            
            // timeval start;
            // timeval end;
            // float dealtime = 0;
            // gettimeofday(&start,NULL);
            // cout<<"fd_isset time:"<<start.tv_sec<<":"<<start.tv_usec<<endl;

			viframe = p_vi->vi_get_frame();
			if(!viframe){
				perror("get frame error!");
				continue;
			}
			ret = ioctl(fd, VIDIOC_DQBUF, &viframe->v4l2_buf);
			if(ret < 0){
				perror("get grame from kernel error!");
				continue;
			}
            //  显示图像
            trigger_emit(SIGNAL_VIFRAME_IN,(void*)viframe);
            // gettimeofday(&end,NULL);
            // dealtime = (end.tv_usec - end.tv_usec) / 1000 + (end.tv_sec - start.tv_sec) * 1000;
            // cout<<"deal time:"<<dealtime<<endl;
#if 1
			ret = ioctl(fd, VIDIOC_QBUF, &viframe->v4l2_buf);
			if(ret < 0){
                perror("release kernel space error!");
                break;
            }
            
			p_vi->vi_put_frame(viframe);
#endif
        }
    }

    return nullptr;
}